<?php
// Η συμβολική σταθερά "KLISTA_FILENAME" περιέχει το όνομα του αρχείου
// που θεωρείται κλειδαριά για το σύστημα· εφόσον το αρχείο αυτό υπάρχει
// το σύστημα θεωρείται κλειστό και, εφόσον πρόκειται για προεσέγγιση
// ιστού, μπορούμε να το προβάλουμε ως ιστοσελίδα.

define("KLISTA_FILENAME", "lock/klista.php");

// Η συμβολική σταθερά "OWNER_ONOMA" περιέχει το ονοματεπώνυμο του κατέχοντος
// το copyright της εφαρμογής.

define("OWNER_ONOMA", "Panos Laskaris");

// Η συμβολική σταθερά "OWNER_EMAIL" περιέχει το email του κατέχοντος
// το copyright της εφαρμογής.

define("OWNER_EMAIL", "plaskari@gmail.com");

// Η συμβολική σταθερά "IPOSTIRIXI" περιέχει το url του κέντρου υποστήριξης
// της εφαρμογής.

define("IPOSTIRIXI", "http://fentasmatakia.net");

mb_internal_encoding("UTF-8");
mb_regex_encoding("UTF-8");

Globals::init();

// Η κλάση "Globals" έχει σκοπό να υιοθετήσει διάφορα global αντικείμενα
// της εφαρμογής, π.χ. το όνομα του server, τον database handler, το login
// name του τρέχοντα χρήστη κλπ. Όλες οι properties και οι μέθοδοι της
// κλάσης είναι static.

class Globals {
	// Η property "init_ok" δείχνει αν έτρεξε η μέθοδος "init".
	// Η μέθοδος πρέπει να τρέχει το πολύ μια φορά.

	private static $init_ok;

	// Η property "session_ok" δείχνει αν έτρεξε η μέθοδος "session".
	// Η μέθοδος πρέπει να τρέχει το πολύ μια φορά.

	private static $session_ok;

	// Η property "server" περιέχει το URL του home directory του server
	// στον οποίο βρίσκεται η εφαρμογή, π.χ. "http://www.fantasmatakia.gr/".
	// Φροντίζουμε να τελειώνει με "/", καθώς θα κολλήσουμε subdirectories
	// και file names προκειμένου να προσπελάσουμε άλλα αρχεία και directories
	// της εφαρμογής.

	public static $server;

	// Η property "skiser" περιέχει το URL του node server
	// στον οποίο βρίσκεται η εφαρμογή.

	public static $skiser;

	// Η property "ip" περιέχει την IP του client που αιτείται τις υπηρεσίες
	// της PHP μέσω HTTP αιτημάτων προς τον web server.

	public static $ip;

	// Η property "www" περιέχει το full pathname του public directory της
	// εφαρμογής στον server, π.χ. "/home/panos/Desktop/fantasmatakia/client",
	// ή "/lampp/opt/htdocs/fantasmatakia/client" και τίθεται αυτόματα από το
	// πρόγραμμα.

	public static $www;

	// Η propery "klista" είναι TRUE/FALSE και είναι αληθής μόνο εφόσον
	// υπάρχει "lock/klista.php" file. Αν υπάρχει τέτοιο file, τότε
	// θεωρούμε ότι ο ιστότοπος βρίσκεται προσωρινά εκτός λειτουργίας.

	public static $klista;

	// Η property "db" είναι ο database handler μέσω του οποίου προσπελαύνουμε
	// την database της εφαρμογής.

	public static $db;

	// Η property "pektis" περιέχει το login name του χρήστη, ή είναι NULL
	// εφόσον ο χρήστης δεν έχει κάνει login.

	public static $pektis;

	// Η property "klidi" περιέχει το κλειδί της τρέχουσας συνεδρίας, ή είναι
	// NULL εφόσον ο χρήστης δεν έχει κάνει login.

	public static $klidi;

	// Η μέθοδος "init" δημιουργεί το (μοναδικό) instance της κλάσης "Globals".
	// Πρόκειται για global αντικείμενο που περιέχει ως properties χρήσιμα στοιχεία
	// της εφαρμογής, π.χ. το login name του τρέχοντα παίκτη κλπ.

	public static function init() {
		if (self::$init_ok) self::klise_fige("init: reinitialization");
		register_shutdown_function("Globals::cleanup");
		self::$init_ok = TRUE;
		self::$session_ok = FALSE;
		self::$server = NULL;
		self::$skiser = NULL;
		self::$ip = NULL;
		self::$www = NULL;
		self::$klista = FALSE;
		self::$db = NULL;
		self::$pektis = NULL;
		self::$klidi = NULL;

		if (!isset($_SERVER)) self::klise_fige("_SERVER: not set");
		if (!is_array($_SERVER)) self::klise_fige("_SERVER: not an array");

		$server_name = array_key_exists("HTTP_HOST", $_SERVER) ? $_SERVER["HTTP_HOST"] : "localhost";
		switch ($server_name) {
		case "127.0.0.1":
		case "localhost":
		case "www.pineza.net":
		case "pineza.net":
			self::$server = "http://" . $server_name . "/fantasmatakia/";
			self::$skiser = "http://" . $server_name . ":";
			break;
		default:
			if ($server_name) print $server_name . ": ";
			self::klise_fige("unknown server");	
		}

		self::get_the_ip();
		self::$www = preg_replace("/lib\/standard\.php$/", "", __FILE__);
		self::$klista = file_exists(self::$www . KLISTA_FILENAME);
	}

	private static function get_the_ip() {
		self::$ip = "";
		if (array_key_exists("REMOTE_ADDR", $_SERVER)) self::$ip = $_SERVER["REMOTE_ADDR"];
		if (!array_key_exists("HTTP_X_FORWARDED_FOR", $_SERVER)) return;

		$ipf = explode(",", $_SERVER["HTTP_X_FORWARDED_FOR"]);
		if (count($ipf)) self::$ip = $ipf[0];
	}

	// Η μέθοδος "database" μας συνδέει με την database.

	public static function database() {
		if (self::is_klista()) return;

		$dbhost = "localhost";
		$dbname = "fantasmatakia";
		$dbuser = "fantasmatakia";

		switch (self::$server) {
		case "http://127.0.0.1/fantasmatakia/";
		case "http://localhost/fantasmatakia/";
		case "http://www.pineza.net/fantasmatakia/";
		case "http://pineza.net/fantasmatakia/";
			break;
		default:
			if (self::$server) print self::$server . ": ";
			self::klise_fige("unknown server (database)");	
		}

		$bekadb = preg_replace("/[^a-zA-Z0-9]/", "", '@p#a@$r*%09##o c$$#@!t@..:');
		self::$db = @mysqli_connect($dbhost, $dbuser, $bekadb);
		if (!self::$db) self::klise_fige("database connection failed (" . @mysqli_connect_error() . ")");

		@mysqli_set_charset(self::$db, "utf8") || self::klise_fige("cannot set character set (database)");
		@mysqli_select_db(self::$db, $dbname) || self::klise_fige("cannot open database (" . $dbname . ")");
	}

	// Η μέθοδος "plires_url" είναι ήσσονος σημασίας και σκοπό έχει τη διευκόλυνση
	// στη γραφή των ονομάτων αρχείων μέσω της πλήρωσης των ονομάτων αυτών με το όνομα
	// του server, π.χ. από "astra" σε "www.fantasmatakia.gr/astra"

	public static function plires_url($fname = "") {
		return(self::$server . $fname);
	}

	// Η μέθοδος "diavase" είναι ήσσονος σημασίας καθώς υποκαθιστά την "require" και
	// μόνο σκοπό έχει την απλοποίηση των pathnames.

	public static function diavase($file) {
		require self::$www . $file;
	}

	// Ακολουθούν μέθοδοι που έχουν να κάνουν με την επώνυμη ή μη χρήση της
	// εφαρμογής.

	public static function is_pektis() {
		return self::$pektis;
	}

	public static function oxi_pektis() {
		return !self::is_pektis();
	}

	public static function pektis_must() {
		if (self::oxi_pektis()) self::klise_fige("ακαθόριστος παίκτης");
		return self::$pektis;
	}

	// Ακολουθούν μέθοδοι που έχουν να κάνουν με τον αν ο ιστότοπος λειτουργεί
	// ή βρίσκεται προσωρινά εκτός λειτουργίας.

	public static function is_klista() {
		return(self::$klista === TRUE);
	}

	public static function oxi_klista() {
		return !self::is_klista();
	}

	// Η μέθοδος "is_akereos" δέχεται μια μεταβλητή και ελέγχει αν είναι ακέραιος.
	// Πρόκειται για απλή, πλήν χρήσιμη utility method.

	public static function is_akereos($x) {
		if (!isset($x)) return FALSE;
		if (!is_numeric($x)) return FALSE;
		return(intval($x) == $x);
	}

	public static function oxi_akereos($x) {
		return !self::is_akereos($x);
	}

	// Η μέθοδος "is_nai" δέχεται ένα string και επιστρέφει true εφόσον πρόκειται
	// για το string "ΝΑΙ", αλλιώς επιστρέφει false. Η μέθοδος κρίθηκε απαραίτητη
	// λόγω των πολλών enumerated παραμέτρων που δέχονται τιμές ΝΑΙ/ΟΧΙ.

	public static function is_nai($x) {
		return($x === "ΝΑΙ");
	}

	public static function oxi_nai($x) {
		return $self::is_nai($x);
	}

	// Η μέθοδος "query" δέχεται ως πρώτη παράμετρο ένα SQL query και το εκτελεί.
	// Αν υπάρξει οποιοδήποτε δομικό πρόβλημα (όχι σχετικό με την επιτυχία ή μη
	// του query), τότε εκτυπώνεται μήνυμα λάθους και το πρόγραμμα σταματά.

	public static function query($query) {
		$result = @mysqli_query(self::$db, $query);
		if ($result) return $result;

		print "SQL ERROR: " . $query . ": " . @mysqli_error(self::$db);
		self::klise_fige(2);
	}

	public static function sql_errno() {
		return mysqli_errno(self::$db);
	}

	public static function sql_error() {
		return mysqli_error(self::$db);
	}

	// Η μέθοδος "first_row" τρέχει ένα query και επιστρέφει την πρώτη γραμμή των
	// αποτελεσμάτων απελευθερώνοντας τυχόν άλλα αποτελέσματα.

	public static function first_row($query, $idx = MYSQLI_BOTH) {
		$result = self::query($query);
		while ($row = @mysqli_fetch_array($result, $idx)) {
			break;
		}

		@mysqli_free_result($result);
		return $row;
	}

	public static function insert_id() {
		return @mysqli_insert_id(self::$db);
	}

	public static function affected_rows() {
		return @mysqli_affected_rows(self::$db);
	}

	public static function autocommit($on_off) {
		@mysqli_autocommit(self::$db, $on_off) || self::klise_fige("autocommit failed");
	}

	public static function commit() {
		@mysqli_commit(self::$db) || self::klise_fige("commit failed");
	}

	public static function rollback() {
		@mysqli_rollback(self::$db) || self::klise_fige("rollback failed");
	}

	// Η μέθοδος "klidoma" επιχειρεί να θέσει κάποιο database lock που καθορίζεται
	// από το tag που περνάμε ως πρώτη παράμετρο. By default η μέθοδος θεωρεί ότι
	// δεν μπορεί να κλειδώσει εφόσον το κλείδωμα αποτύχει για 2 δευτερόλεπτα, αλλά
	// μπορούμε να περάσουμε μεγαλύτερο ή μικρότερο χρονικό διάστημα ως δεύτερη
	// παράμετρο.
	//
	// Η μέθοδος επιστρέφει TRUE εφόσον το κλείδωμα επιτύχει, αλλιώς επιστρέφει
	// FALSE.

	public static function klidoma($tag, $timeout = 2) {
		$query = "SELECT GET_LOCK(" . self::asfales_sql($tag) . ", " . $timeout . ")";
		$row = self::first_row($query, MYSQLI_NUM);
		if (!$row) return FALSE;
		return($row[0] == 1);
	}

	// Η μέθοδος "xeklidoma" ξεκλειδώνει κάποιο κλείδωμα που θέσαμε με την μέθοδο
	// "klidoma". Το tag του κλειδώματος που θα ξεκλειδωθεί περνιέται ως πρώτη
	// παράμετρος, ενώ ως δεύτερη παράμετρος μπορεί να περάσει TRUE/FALSE value
	// που δείχνει αν πριν το ξεκλείδωμα θα γίνει commit ή rollback αντίστοιχα.
	// Αν δεν περαστεί δεύτερη παράμετρος, τότε δεν γίνεται ούτε commit ούτε
	// rollback, οπότε μπορούμε να συνεχίσουμε στα πλαίσια της τρέχουσας
	// transaction.

	public static function xeklidoma($tag, $commit = NULL) {
		if (isset($commit)) {
			if ($commit) @mysqli_commit(self::$db);
			else @mysqli_rollback(self::$db);
		}

		$query = "DO RELEASE_LOCK(" . self::asfales_sql($tag) . ")";
		@mysqli_query(self::$db, $query);
	}

	// Η μέθοδος "cleanup" κλείνει τη σύνδεση με την database και κάνει flush
	// τυχόν buffered output.

	public static function cleanup() {
		if (isset(self::$db)) {
			@mysqli_kill(self::$db, @mysqli_thread_id(self::$db));
			@mysqli_close(self::$db);
		}

		while (@ob_end_flush());
	}

	// Η μέθοδος "klise_fige" κλείνει τη σύνδεση με την database και διακόπτει
	// το πρόγραμμα. Μπορούμε να περάσουμε μήνυμα το οποίο θα εκτυπωθεί πριν τη
	// διακοπή του προγράμματος. Μπορούμε, ακόμη, να περάσουμε ως παράμετρο μια
	// αριθμητική τιμή η οποία θα χρησιμοποιηθεί ως exit status.

	public static function klise_fige($msg = NULL) {
		if (!isset($msg)) {
			$stat = 0;
		}
		elseif (is_int($msg)) {
			$stat = (int)$msg;
		}
		else {
			print $msg;
			$stat = 2;
		}

		die($stat);
	}

	// Η μέθοδος "perastike" δέχεται ως παράμετρο ένα string και επιστρέφει
	// TRUE εφόσον έχει περαστεί αντίστοιχη GET/POST παράμετρος.

	public static function perastike($key) {
		return(isset($_REQUEST) && is_array($_REQUEST) && array_key_exists($key, $_REQUEST));
	}

	public static function den_perastike($key) {
		return !self::perastike($key);
	}

	// Η μέθοδος "perastike_must" επιτάσσει να έχει περαστεί η GET/POST παράμετρος που
	// περνάμε ως πρώτη παράμετρο. Αν έχει περαστεί η παράμετρος, τότε επιστρέφεται η
	// τιμή της παραμέτρου, αλλιώς το πρόγραμμα σταματά.

	public static function perastike_must($key, $msg = NULL) {
		if (self::perastike($key)) return $_REQUEST[$key];

		print isset($msg) ? $msg : $key . ": δεν περάστηκε παράμετρος";
		self::klise_fige(2);
	}

	// Η μέθοδος "session_set" δέχεται ως παράμετρο ένα string και επιστρέφει
	// TRUE εφόσον υπάρχει το αντίστοιχο session cookie.

	public static function session_set($tag) {
		if (!isset($_SESSION)) self::klise_fige("_SESSION: not set");
		if (!is_array($_SESSION)) self::klise_fige("_SESSION: not an array");

		return array_key_exists($tag, $_SESSION);
	}

	// Η μέθοδος "asfales_sql" δέχεται ένα string και επιστρέφει το ίδιο string
	// αλλά τροποποιημένο ώστε να μην τίθεται θέμα SQL injection. Γίνεται επίσης
	// και διαφυγή των quotes. Το string επιστρέφεται μαζί με τα quotes που το
	// περικλείουν, εκτός και αν περάσουμε δεύτερη (false) παράμετρο.

	public static function asfales_sql($s, $string = TRUE) {
		if (get_magic_quotes_gpc()) $s = stripslashes($s);
		if (!isset(self::$db)) self::klise_fige("asfales_sql: database not open");
		$s = @mysqli_real_escape_string(self::$db, $s);
		return($string ? "'" . $s . "'" : $s);
	}

	// Η μέθοδος "asfales_json" δέχεται ως παράμετρο ένα string και το επιστρέφει
	// τροποιημένο ώστε να μπορεί με ασφάλεια να ενταχθεί ως rvalue σε json objects
	// μαζί με τα quotes.

	public static function asfales_json($s) {
		$s = str_replace('\\', '\\\\', $s);
		return "'" . str_replace("'", "\'", $s) . "'";
	}

	// Η μέθοδος "akirosi_script" χρησιμοποιείται για να ακυρώσει τυχόν
	// ενσωματωμένο javascript κώδικα σε μηνύματα και συζητήσεις, και
	// το επιτυγχάνει εισάγοντας χαρακτήρα μηδενικού πλάτους πριν τη
	// λέξη script.

	public static function akirosi_script($s) {
		return preg_replace("/script/i", "&#8203;script", $s);
	}

	public static function email_check($email) {
		return filter_var($email, FILTER_VALIDATE_EMAIL) ? $email : NULL;
	}

	public static function header_data() {
		header('Content-type: text/plain; charset=utf-8');
	}

	public static function header_json() {
		header('Content-Type: application/json; charset=utf-8');
	}

	public static function header_html() {
		header('Content-type: text/html; charset=utf-8');
	}

	public static function header_error($minima = "server error") {
		header("HTTP/1.0 500 " . $minima);
	}

	// Η μέθοδος "session" ενεργοποιεί το session και καλείται by default
	// σε όσες σελίδες κατασκευάζονται με τις μεθόδους της κλάσης "Selida".

	public static function session() {
		if (self::$session_ok) self::klise_fige("session: reinitialization");
		self::$session_ok = TRUE;

		// 24 * 7 * 3600 = 604800
		ini_set("session.gc_maxlifetime", "604800");
		session_set_cookie_params(604800);
		session_start();

/*
$_SESSION['pektis'] = 'panos'; $_SESSION['klidi'] = 'XXX';
unset($_SESSION['pektis']); unset($_SESSION['klidi']);
*/
		// Αν ο ιστότοπος είναι κλειστός αποφεύγουμε την επώνυμη χρήση
		// και μάλιστα καταργούμε και τα σχετικά διαπιστευτήρια.

		if (self::is_klista()) {
			self::exodos();
		}
		else {
			if (self::session_set("pektis")) self::$pektis = $_SESSION["pektis"];
			if (self::session_set("klidi")) self::$klidi = $_SESSION["klidi"];
		}
	}

	public static function exodos() {
		//Sinedria::arxio() || Globals::klise_fige();

		self::$pektis = NULL;
		self::$klidi = NULL;

		if (!isset($_SESSION)) return;
		if (!is_array($_SESSION)) return;

		unset($_SESSION["pektis"]);
		unset($_SESSION["klidi"]);
	}

	// Η μέθοδος "random_string" επιστρέφει ένα string συγκεκριμένου μήκους, αποτελούμενο
	// από χαρακτήρες που λαμβάνονται από παλέτα.

	public static function random_string($mikos, $paleta =
		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") {
		$s = "";
		$n = strlen($paleta) - 1;
		for ($i = 0; $i < $mikos; $i++) {
			$s .= $paleta[mt_rand(0, $n)];
		}

		return $s;
	}
}
?>
